const { userCollection, LOG_TYPE } = require("../../common/constants");
const { ERROR } = require("../../common/error");
const { findUser } = require("./account");
const { getValidInviteCode, generateInviteInfo } = require("./fission");
const { logout } = require("./logout");
const PasswordUtils = require("./password");
const { merge } = require("../npm/index");

async function realPreRegister(params = {}) {
  const { user } = params;
  const { userMatched } = await findUser({
    userQuery: user,
    authorizedApp: this.getUniversalClientInfo().appId,
  });
  if (userMatched.length > 0) {
    throw {
      errCode: ERROR.ACCOUNT_EXISTS,
    };
  }
}

async function preRegister(params = {}) {
  try {
    await realPreRegister.call(this, params);
  } catch (error) {
    await this.middleware.uniIdLog({
      success: false,
      type: LOG_TYPE.REGISTER,
    });
    throw error;
  }
}

async function preRegisterWithPassword(params = {}) {
  const { user, password } = params;
  await preRegister.call(this, {
    user,
  });
  const passwordUtils = new PasswordUtils({
    clientInfo: this.getUniversalClientInfo(),
    passwordSecret: this.config.passwordSecret,
  });
  const { passwordHash, version } = passwordUtils.generatePasswordHash({
    password,
  });
  const extraData = {
    password: passwordHash,
    password_secret_version: version,
  };
  return {
    user,
    extraData,
  };
}

async function thirdPartyRegister({ user = {} } = {}) {
  return {
    mobileConfirmed: !!(user.mobile && user.mobile_confirmed) || false,
    emailConfirmed: !!(user.email && user.email_confirmed) || false,
  };
}

async function postRegister(params = {}) {
  const { user, extraData = {}, isThirdParty = false, inviteCode } = params;
  const {
    appId,
    appName,
    appVersion,
    appVersionCode,
    channel,
    scene,
    clientIP,
    osName,
  } = this.getUniversalClientInfo();
  const uniIdToken = this.getUniversalUniIdToken();

  merge(user, extraData);

  const registerChannel = channel || scene;
  user.register_env = {
    appid: appId || "",
    uni_platform: this.clientPlatform || "",
    os_name: osName || "",
    app_name: appName || "",
    app_version: appVersion || "",
    app_version_code: appVersionCode || "",
    channel: registerChannel ? registerChannel + "" : "", // channel可能为数字，统一存为字符串
    client_ip: clientIP || "",
  };

  user.register_date = Date.now();
  user.dcloud_appid = [appId];

  if (user.username) {
    user.username = user.username.toLowerCase();
  }
  if (user.email) {
    user.email = user.email.toLowerCase();
  }

  const {
    autoSetInviteCode, // 注册时自动设置邀请码
    forceInviteCode, // 必须有邀请码才允许注册，注意此逻辑不可对admin生效
    userRegisterDefaultRole, // 用户注册时配置的默认角色
  } = this.config;
  if (autoSetInviteCode) {
    user.my_invite_code = await getValidInviteCode();
  }

  // 如果用户注册默认角色配置存在且不为空数组
  if (userRegisterDefaultRole && userRegisterDefaultRole.length) {
    // 将用户已有的角色和配置的默认角色合并成一个数组，并去重
    user.role = Array.from(
      new Set([...(user.role || []), ...userRegisterDefaultRole]),
    );
  }

  const isAdmin = user.role && user.role.includes("admin");

  if (forceInviteCode && !isAdmin && !inviteCode) {
    throw {
      errCode: ERROR.INVALID_INVITE_CODE,
    };
  }

  if (inviteCode) {
    const { inviterUid, inviteTime } = await generateInviteInfo({
      inviteCode,
    });
    user.inviter_uid = inviterUid;
    user.invite_time = inviteTime;
  }

  if (uniIdToken) {
    try {
      await logout.call(this);
    } catch (error) {}
  }

  const beforeRegister = this.hooks.beforeRegister;
  let userRecord = user;
  if (beforeRegister) {
    userRecord = await beforeRegister({
      userRecord,
      clientInfo: this.getUniversalClientInfo(),
    });
  }

  const { id: uid } = await userCollection.add(userRecord);

  const createTokenRes = await this.uniIdCommon.createToken({
    uid,
  });

  const { errCode, token, tokenExpired } = createTokenRes;

  if (errCode) {
    throw createTokenRes;
  }

  await this.middleware.uniIdLog({
    data: {
      user_id: uid,
    },
    type: LOG_TYPE.REGISTER,
  });

  return {
    errCode: 0,
    uid,
    newToken: {
      token,
      tokenExpired,
    },
    ...(isThirdParty
      ? thirdPartyRegister({
          user: {
            ...userRecord,
            _id: uid,
          },
        })
      : {}),
    passwordConfirmed: !!userRecord.password,
  };
}

module.exports = {
  preRegister,
  preRegisterWithPassword,
  postRegister,
};
